#!/usr/bin/python
# -*- coding: utf-8 -*-

# This script re-generate the configurations files of cisco switchs
# from the data stored inside MYSQL database.


# Pour envouyer la nouvelles conf sur les switchs
# 	executer sql2switchs en root
# 	se placer dans /srv/tftp/switchs
#	executer /opt/rezome/switchs_scripts/load_config.py
# 	executer /opt/rezome/switchs_scripts/copy_running_config_to_startup_config.py
# 	se connecter au switch et executer la commande reload pour rechager le config

################################################################################
# Configuration

# Chemin ou son générées les configurations des switchs
GEN_PATH = '/srv/tftp/switchs/'
#GEN_PATH = './confs/'

INCLUDE_PATH =	"/opt/rezome/inc"

# On définit la liste des vlans avec les numéros d'ACL correspondants et le nom
# /!\ En cas de modification des num d'ACL, modifier aussi les ACL corespondantes 
#     dans la partie génération des ports 100 Mégas.
liste_vlans = [ ('AE',68,""),
		('UTBM',4,""),
		('UTBM-PART',11,''),
		('MEUH',100,110),
		('Captif',200,120),
		('Boulet',250,125)]


################################################################################ 

__author__ = "Gautier Risch <gautier.risch@utbm.fr>"
__version__ = "0.4"


import os, sys
import MySQLdb
sys.path.append(INCLUDE_PATH)
from mysql import *


# Génération de l'entête des fichiers de confs
def gen_header(hostname):
	return """version 12.1
no service pad
service tcp-keepalives-in
service timestamps debug uptime
service timestamps log datetime
service password-encryption
service sequence-numbers
!
hostname %(hostname)s
!
enable secret 5 $1$xGPC$S4scErBSHGbWe0YI10Y4J/
!
clock timezone CEST 2
errdisable recovery cause udld
errdisable recovery cause bpduguard
errdisable recovery cause security-violation
errdisable recovery cause channel-misconfig
errdisable recovery cause pagp-flap
errdisable recovery cause dtp-flap
errdisable recovery cause link-flap
errdisable recovery cause psecure-violation
errdisable recovery cause dhcp-rate-limit
errdisable recovery cause unicast-flood
errdisable recovery cause vmps
errdisable recovery cause loopback
ip subnet-zero
ip dhcp relay forward spanning-tree
!
ip dhcp snooping vlan 68
ip dhcp snooping vlan 100
ip dhcp snooping vlan 200
ip dhcp snooping vlan 250
ip dhcp snooping
no ip domain-lookup
!
!
spanning-tree mode rapid-pvst
no spanning-tree optimize bpdu transmission
spanning-tree extend system-id
!
!
!
!
""" % locals()

####################################
# Ports
####################################

# Configuration d'un port générique avec des ACL generiques
def gen_port_generique(portnumber,num_prise): 
	return """interface FastEthernet0/%(portnumber)d
 description NOM_VLAN - %(num_prise)s
 switchport access vlan NUM_VLAN
 switchport mode access
 ip access-group NUM_ACL in
 spanning-tree guard root
 spanning-tree portfast
 no ip dhcp snooping trust
!
""" % locals()


# Génération de la configuration d'un port sur le vlan AE (68) donc sans ACL
def gen_vlan_ae(portnumber,num_prise):
	return """interface FastEthernet0/%(portnumber)d
 description AE - %(num_prise)s
 switchport access vlan 68
 switchport mode access
 spanning-tree guard root
 spanning-tree portfast
 no ip dhcp snooping trust
 no ip access-group 110 in
 no ip access-group 120 in
 no ip access-group 125 in
!
""" % locals()

# Génération de la configuration d'un port sur le vlan UTBM (4)
# VLAN anciennement utillisé pour la zone portable démonté en MAI 2010
def gen_port_utbm_portable(portnumber,num_prise):
	return """interface FastEthernet0/%(portnumber)d
 description UTBM - Z PORTABLE %(num_prise)s
 switchport access vlan 4
 switchport mode access
 spanning-tree guard root
 no spanning-tree portfast
 no ip dhcp snooping trust
 no spanning-tree link-type point-to-point
 no ip access-group 110 in
 no ip access-group 120 in
 no ip access-group 125 in
!
""" % locals()

# Génération de la configuration d'un port sur le vlan UTBM (4)
# VLAN anciennement utillisé pour la zone portable démonté en MAI 2010
# Config spéciale pour l'arrivé de la zone sur le switch
def gen_port_utbm_portable_input(portnumber,num_prise):
	return """interface FastEthernet0/%(portnumber)d
 description INPUT ZONE PORTABLE %(num_prise)s
 switchport access vlan 4
 switchport mode access
 no spanning-tree guard root
 no spanning-tree portfast
 no ip dhcp snooping trust
 no spanning-tree link-type point-to-point
 no ip access-group 110 in
 no ip access-group 120 in
 no ip access-group 125 in
!
""" % locals()

# Génération de la configuration d'un port sur le vlan UTBM 1)
# Zone partenaire
def gen_port_utbm_partenaire(portnumber,num_prise):
	return """interface FastEthernet0/%(portnumber)d
 description UTBM - PARTENAIRE %(num_prise)s
 switchport access vlan 11
 switchport mode access
 spanning-tree guard root
 no spanning-tree portfast
 no ip dhcp snooping trust
 no spanning-tree link-type point-to-point
 no ip access-group 110 in
 no ip access-group 120 in
 no ip access-group 125 in
!
""" % locals()

# Génération de la configuration d'un port sur le vlan UTBM 1)
# Configuration uniquement utilisée pour l'arrivé de la zone partenaire
# ( no spanning-tree guard root )
def gen_port_utbm_partenaire_input(portnumber,num_prise):
	return """interface FastEthernet0/%(portnumber)d
 description UTBM INPUT Z PARTENAIRE %(num_prise)s
 switchport access vlan 11
 switchport mode access
 no spanning-tree guard root
 no spanning-tree portfast
 no ip dhcp snooping trust
 no spanning-tree link-type point-to-point
 no ip access-group 110 in
 no ip access-group 120 in
 no ip access-group 125 in
!
""" % locals()

# Génération de la configuration d'un port trunk
def gen_trunk(portnumber,num_prise):
	return """interface FastEthernet0/%(portnumber)d
 description TRUNK - %(num_prise)s
 switchport mode trunk
 spanning-tree portfast
 ip dhcp snooping trust
 no ip access-group 110 in
 no ip access-group 120 in
 no ip access-group 125 in
!
""" % locals()

# Génération de la configuration d'un port gigabit trunk inter-switchs
def gen_gigabit(portnumber):
	return """interface GigabitEthernet0/%(portnumber)d
 switchport mode trunk
 spanning-tree link-type point-to-point
 ip dhcp snooping trust
!
""" % locals()

# Génération de la conf d'un port pour relier la freebox (dhcp snooping trust)
# Inutilisé car désormais la FBX passe par le SLM2008 relié en DUR.
def gen_port_freebox(portnumber):
	return """interface FastEthernet0/%(portnumber)d
 description UPLINK FBX
 spanning-tree guard root
 spanning-tree portfast
 switchport access vlan 68
 switchport mode access
 ip dhcp snooping trust
 no ip access-group in
 no shutdown
 no ip access-group 110 in
 no ip access-group 120 in
 no ip access-group 125 in
!
""" % locals()

# Configuration d'un port sur le vlan d'admin
def gen_port_admin(portnumber,num_prise):
	return """interface FastEthernet0/%(portnumber)d
 description ADMIN - %(num_prise)s
 switchport access vlan 1
 switchport mode access
 spanning-tree guard root
 spanning-tree portfast
 no shutdown
 no ip dhcp snooping trust
 no ip access-group 110 in
 no ip access-group 120 in
 no ip access-group 125 in
!
""" % locals()

####################################
# VLAN
####################################

#Génération de la confs des vlans -> on genere tous les vlans des switchs
def gen_vlans(vlans_liste,switch_ip):
	cts = gen_default_vlan(switch_ip)
	for (nom_vlan,num_vlan,acl_vlan) in vlans_liste:
		#print "NOM :" + nom_vlan + "\t Numéro VLAN : " + str(num_vlan) + "\t Num ACL : " + str(acl_vlan) 
		if acl_vlan != "":		
			# Génération d'un VLAN avec des ACLs
			cts += gen_vlan_with_acl(num_vlan,nom_vlan,acl_vlan)
		else:
			# Génération d'un VLAN sans ACLs
			cts += gen_vlan_without_acl(num_vlan,nom_vlan)
	return cts

# Génération de la configuration d'un port sur le vlan d'admin qui porte l'ip d'admin 
def gen_default_vlan(ip_switch):
	return """interface Vlan1
 description ADMIN
 ip address %(ip_switch)s 255.255.255.0
 no ip route-cache
!
""" % locals()

# Génération d'un VLAN sans ACL
def gen_vlan_without_acl(vlan_number, vlan_name):
	return """interface Vlan%(vlan_number)i
 description %(vlan_name)s
 no ip address
 no ip route-cache
 shutdown
!
""" % locals()

# Génération d'un VLAN avec ACL
def gen_vlan_with_acl(vlan_number, vlan_name, acl_number):
	return """interface Vlan%(vlan_number)i
 description %(vlan_name)s
 no ip address
 ip access-group %(acl_number)i in
 ip access-group %(acl_number)i out
 no ip route-cache
 shutdown
!
""" % locals()


####################################
# ACL
####################################


# Génération des ACL
def gen_acls():
	anti_spoof = """access-list ACL permit udp any eq bootpc any eq bootps
access-list ACL permit udp any eq bootps any eq bootpc
access-list ACL deny ip 0.0.0.0          0.255.255.255   any
access-list ACL deny ip 10.0.0.0         0.255.255.255   any
access-list ACL deny ip 127.0.0.0        0.255.255.255   any
access-list ACL deny ip 169.254.0.0      0.0.255.255     any
access-list ACL deny ip 172.16.0.0       0.15.255.255    any
access-list ACL deny ip 192.0.2.0        0.0.0.255       any
access-list ACL deny ip 192.168.0.0      0.0.255.255     any
access-list ACL deny ip 224.0.0.0        15.255.255.255  any
access-list ACL deny ip 240.0.0.0        7.255.255.255   any
access-list ACL deny ip 248.0.0.0        7.255.255.255   any
access-list ACL deny ip 255.255.255.255  0.0.0.0         any
access-list ACL permit ip any any"""

	return """no ip http server
logging trap debugging
logging 192.168.254.253
logging console debugging
logging monitor debugging
! http://www.cisco.com/en/US/docs/switches/lan/catalyst2950/software/release/12.1_9_ea1/configuration/guide/swlog.html
!
access-list 110 deny ip 192.168.0.0 0.0.255.255 any
access-list 110 permit ip any any
!
access-list 112 permit ip 172.20.0.0 0.0.255.255 any
%s
!
access-list 120 permit ip host 192.168.200.1 any
access-list 120 permit udp any any
access-list 120 deny ip any any
!
access-list 125 permit ip host 192.168.250.1 any
access-list 125 permit udp any any
access-list 125 deny ip any any""" % anti_spoof.replace('ACL', '112')





# Génération de la fin du fichier de configuration
def gen_end():
	return """
!
snmp-server community r3z0m3 RO
!
line con 0
 exec-timeout 0 0
line vty 0 4
 password 7 097B62304A2D4039
 login
line vty 5 15
 password 7 063123181F665E32
 login
!
ntp clock-period 17180055
ntp server 192.168.254.253
ntp server 192.168.254.252
!
end
"""

#
# vlan ->    num_vlan id_vlan	acl	
# admin -> 	1	1	
# MEUH -> 	100	2	110 ( ou 112 : plus restrictives et a tester )
# captif -> 	200	3	120
# boulet -> 	250	4	125
# ae -> 	68	5	/
# trunk ->	0	6	/
# UTBM  -> 	4	7	/
# UTBM-PAR -> 	11	8	/ ZONE partenaire de l'UTBM


def main():




	db = MySQLdb.connect(host=MYSQL_HOST, user=MYSQL_USER, passwd=MYSQL_PASSWD, db=MYSQL_DB)

	c = db.cursor()
	c.execute("SELECT nom_switch, ipv4_switch FROM switch WHERE actif_switch='1';")
	rows = c.fetchall()
	for nom_switch, ip_switch in rows:

		# On génère le début du fichier de conf pour le switch concerné
		data = gen_header(nom_switch)

		# print "Switch : " + nom_switch + " IP : " + ip_switch
		c.execute("""SELECT port.num_port, vlan.num_vlan, chambre.num_chambre FROM switch INNER JOIN port ON port.id_switch = switch.id_switch INNER JOIN vlan ON vlan.id_vlan = port.id_vlan LEFT JOIN chambre ON port.id_port = chambre.id_port WHERE switch.nom_switch=%s ;""",(nom_switch))

		ports_switch = c.fetchall()

		# Génération des ports 100 mégas des switchs
		for port, vlan, num_prise in ports_switch:
			# Port -> numéro du port physique sur le switch
			# vlan -> numéro du vlan corespondant au port
			# num_prise -> numéro de la prise coté utilisateur ( corespond au numéro
			# 	de la chambre si celle ci est bien indiquée.

			# print "Switch :" + nom_switch + " port : " + str(port) + " Vlan : " + str(vlan)

			if "None" in str(num_prise):
				# Pour ne pas afficher "None" dans la conf, on met num_prise à vide
				num_prise = ""

			if int(vlan) == 0 :
				# Traitement d'un port trunk
				data += gen_trunk(port,num_prise)
			elif int(vlan) == 1 :			
				# Traitement d'un port d'admin
				data += gen_port_admin(port,num_prise)
			elif int(vlan) == 68 :
				# Traitement d'un port sur le VLAN AE (FBX)
				# La FBX est désormais sur le SLM2008 => plus besoin de configurer de port spécial
				#if nom_switch == 'DALTONS-1' and port == 24:
					# Daltons 1 port 24 -> gen_port_freebox 
					#	pour ip dhcp snooping trust
					# Port sur lequel est relié la FreeBOX 
					# 	(autoriser le DHCP depuis le port)
				#	data += gen_port_freebox(port)
				#else:
				data += gen_vlan_ae(port,num_prise)
			elif int(vlan) == 4 :
				data += gen_port_utbm_portable(port,num_prise)
				# On regarde si on traite le port d'arrivée de la zone portable
				# ( démantelé au profit de la zone partenaire ) si oui => config particulière
				if nom_switch == 'DALTONS-1' and port == 17:
					data += gen_port_utbm_portable_input(port,num_prise)
				else:
					data += gen_port_utbm_portable(port,num_prise)
			elif int(vlan) == 11 :
				# On regarde si on traite le port d'arrivée de la zone partenaire
				# ( anciennement zone portable ) si oui => config particulière
				if nom_switch == 'DALTONS-1' and port == 17:
					data += gen_port_utbm_partenaire_input(port,num_prise)
				else:
					data += gen_port_utbm_partenaire(port,num_prise)
				
                        elif int(vlan) == 200 :
				# Traitement d'un port sur le VLAN du portail captif
                                data += gen_port_generique(port,num_prise).replace('NUM_VLAN','200').replace('NUM_ACL','120').replace('NOM_VLAN',"CAPTIF")
                        elif int(vlan) == 250 :
				# Traitement d'un port sur le VLAN BOULET ( port bloqué )
                                data += gen_port_generique(port,num_prise).replace('NUM_VLAN','250').replace('NUM_ACL','125').replace('NOM_VLAN',"BOULET")
			elif int(vlan) == 100 :
				# Traitement d'un port sur le VLAN 100
                                data += gen_port_generique(port,num_prise).replace('NUM_VLAN','100').replace('NUM_ACL','110').replace('NOM_VLAN',"MEUH")
			else:
				# Traitement d'un port sur un VLAN NON GERE !!! ->ERREUR
				#os.system("""mail -s "sql2switch_confs" root bleh""")
				sys.stderr.write("""sql2switchs_conf : Le port""" + str(port) + """ sur le vlan""" + str(vlan)  + """ a posé un problème de traitement, programme interrompu""")
				sys.exit(1)

		# Génération des 2 ports gigabits
		data += gen_gigabit(1)
		data += gen_gigabit(2)

		# Génération des vlans
		data += gen_vlans(liste_vlans,ip_switch)

		# Génération des ACL et de la fin de la conf générique
		data += gen_acls() + gen_end()

		# ecriture du fichier de conf
		fp = open(GEN_PATH + nom_switch + '.config', 'w')
		fp.write(data)
		fp.close()

	sys.exit(0)

main()
